/*
 * Copyright (C) 2008 Nokia Corporation.
 * Copyright (C) 2008 University of Szeged, Hungary
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * Authors: Artem Bityutskiy
 *          Adrian Hunter
 *          Zoltan Sogor
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "lzo/lzo1x.h"
//#include <linux/types.h>

#define crc32 __zlib_crc32
#include "zlib/zlib.h"
#undef crc32

#include "defs.h"
#include "decompr.h"
#include "ubifs-media.h"

#define INFLATE_DEF_WINBITS   11

static unsigned long long errcnt = 0;

static int no_compress(void *in_buf, size_t in_len, void *out_buf,
		       size_t *out_len)
{
	memcpy(out_buf, in_buf, in_len);
	*out_len = in_len;
	return 0;
}

static int zlib_inflate(void *in_buf, size_t in_len, void *out_buf,
			size_t *out_len)
{
	z_stream strm;
	int ret;

	strm.zalloc = NULL;
	strm.zfree = NULL;

	if (inflateInit2(&strm, -INFLATE_DEF_WINBITS)) {
		errcnt += 1;
		return -1;
	}

	strm.next_in = in_buf;
	strm.avail_in = in_len;
	strm.total_in = 0;

	strm.next_out = out_buf;
	strm.avail_out = *out_len;
	strm.total_out = 0;

	ret = inflate(&strm, Z_FINISH);
	if (ret != Z_STREAM_END) {
		inflateEnd(&strm);
		errcnt += 1;
		return -1;
	}

	if (inflateEnd(&strm) != Z_OK) {
		errcnt += 1;
		return -1;
	}

	*out_len = strm.total_out;

	return 0;
}

static int lzo_decompress(void *in_buf, size_t in_len, void *out_buf,
			size_t *out_len)
{
	lzo_uint len;
	int ret;

	len = *out_len;
	ret = lzo1x_decompress(in_buf, in_len, out_buf, &len, NULL);
	*out_len = len;

	if (ret != LZO_E_OK) {
		errcnt += 1;
		return -1;
	}

	return 0;
}

int decompress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len,
		  int type)
{
	int ret;

	switch (type) {
	case UBIFS_COMPR_NONE:
		ret = no_compress(in_buf, in_len, out_buf, out_len);
		break;
	case UBIFS_COMPR_LZO:
		ret = lzo_decompress(in_buf, in_len, out_buf, out_len);
		break;
	case UBIFS_COMPR_ZLIB:
		ret = zlib_inflate(in_buf, in_len, out_buf, out_len);
		break;
	default:
		ret = -1;
		break;
	}

	return ret;
}

int init_compression(void)
{
	return 0;
}

void destroy_compression(void)
{
	if (errcnt)
		fprintf(stderr, "%llu compression errors occurred\n", errcnt);
}
